﻿using Heroius.Extension;
using Microsoft.Win32;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using WinForm = System.Windows.Forms;
using System.Xml.Linq;
using System.Windows.Threading;
using System.IO;

namespace Heroius.BIU
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();

            var path = $"{Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)}/Heroius";
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            path = $"{path}/BIU";
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            path = $"{path}/logs";
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }

            Engine = new BIUEngine();
            Engine.OnExecutionNotify += Engine_OnExecutionNotify;
            Plan = new BIUPlan();
            Engine.Plan = Plan;

            diaload = new OpenFileDialog() { Filter = "BIU计划文件|*.biu" };
            diasave = new SaveFileDialog() { Filter = "BIU计划文件|*.biu" };
            diafolder = new System.Windows.Forms.FolderBrowserDialog();

            Logs = new ObservableCollection<string>();

            DataContext = this;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        void RaisePropertyChangedEvent(string PropertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
        }

        public BIUEngine Engine { get; private set; }
        public BIUPlan Plan { get; private set; }

        #region Log

        public ObservableCollection<string> Logs { get; set; }

        public StreamWriter LogWriter;
        void BeginLog()
        {
            Logs.Clear();
            var path = $"{Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)}/heroius/biu/logs/{DateTime.Now.ToString("yyyyMMddHHmmss")}.log";
            LogWriter = new StreamWriter(File.Open(path, FileMode.OpenOrCreate), Encoding.Default);
        }
        void WriteLog(string content)
        {
            Logs.Add(content); LogView.ScrollToBottom();
            LogWriter.WriteLine(content);
        }
        void FinishLog()
        {
            LogWriter.Close();
        }

        private void Engine_OnExecutionNotify(object sender, BIUExecutionNotifyArgs e)
        {
            this.Dispatcher.Invoke(new Action(() => { WriteLog(e.LogContent); }));
        }

        #endregion

        private void Run_Click(object sender, RoutedEventArgs e)
        {
            var rec = LoadRecord();
            BeginLog();
            GrdBusy.Visibility = Visibility.Visible;
            var asyncAction = new Func<BIURecord, BIURecord>(Engine.Execute);
            asyncAction.BeginInvoke(rec, new AsyncCallback((asyncResult) =>
            {
                this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action<IAsyncResult>((asyncResult2) =>
                {
                    var result = asyncAction.EndInvoke(asyncResult2);
                    GrdBusy.Visibility = Visibility.Collapsed;
                    FinishLog();
                    MessageBox.Show(result.RunMessage, result.Result.ToString());
                    if (Directory.Exists(Plan.To)) //额外的判断，用于未执行，或非拷贝性执行的情况（预留批量处理功能）
                    {
                        SaveRecord(result);
                    }

                    // ui update
                }), asyncResult);

            }), null);
        }

        #region Plan IO

        SaveFileDialog diasave;
        private void Save_Click(object sender, RoutedEventArgs e)
        {
            if (diasave.ShowDialog() == true)
            {
                new XDocument(new XComment($"Generated by Heroius.BIU, version={Assembly.GetExecutingAssembly().GetName().Version.ToString(4)}"), new XElement("BIUPlan",
                    new XElement("From", Plan.From), new XElement("To", Plan.To), 
                    new XElement("Rules", Plan.Rules.Select(r => new XElement("add", new XAttribute("State", r.State), new XAttribute("Execution", r.Execution), new XElement("Custom", r.CustomExp), new XElement("Filter", r.FilterExp)))),
                    new XElement("Variables", Plan.Variables.Select(v=>new XElement("add", new XAttribute("Key", v.Value1), new XAttribute("Value", v.Value2)))))).Save(diasave.FileName);
            }
        }
        OpenFileDialog diaload;
        private void Load_Click(object sender, RoutedEventArgs e)
        {
            if (diaload.ShowDialog() == true)
            {
                var root = XDocument.Load(diaload.FileName).Element("BIUPlan");
                Plan.From = root.Element("From").Value;
                Plan.To = root.Element("To").Value;
                Plan.Rules.Clear();
                var rs = root.Element("Rules").Elements("add");
                if (rs != null)
                {
                    foreach (var r in rs)
                    {
                        Plan.Rules.Add(new Rule() { State = r.Attribute("State").Value.As<FileState>(), Execution = r.Attribute("Execution").Value.As<ExecutionOption>(), FilterExp = r.Element("Filter").Value, CustomExp = (r.Element("Custom") == null)?"":r.Element("Custom").Value });
                    }
                }
                Plan.Variables.Clear();
                if (root.Element("Variables") != null)
                {
                    var vs = root.Element("Variables").Elements("add");
                    foreach (var v in vs)
                    {
                        Plan.Variables.Add(new ValuePair<string, string>(v.Attribute("Key").Value, v.Attribute("Value").Value));
                    }
                }
            }
        }

        #endregion

        #region Rule Edit

        private void AddRule_Click(object sender, RoutedEventArgs e)
        {
            Plan.Rules.Add(new Rule());
        }

        private void DelRule_Click(object sender, RoutedEventArgs e)
        {
            var r = (sender as Button).DataContext as Rule;
            Plan.Rules.Remove(r);
        }

        #endregion

        #region Source/Target

        WinForm.FolderBrowserDialog diafolder;

        private void BrowseFrom_Click(object sender, RoutedEventArgs e)
        {
            if (diafolder.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                Plan.From = diafolder.SelectedPath;
            }
        }

        private void BrowseTo_Click(object sender, RoutedEventArgs e)
        {
            if (diafolder.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                Plan.To = diafolder.SelectedPath;
            }
        }

        #endregion

        #region Log View

        private void Log_Click(object sender, RoutedEventArgs e)
        {
            BtnCloseLog.Visibility = Visibility.Visible;
            GrdBusy.Visibility = Visibility.Visible;
        }

        private void CloseLog_Click(object sender, RoutedEventArgs e)
        {
            GrdBusy.Visibility = Visibility.Collapsed;
            BtnCloseLog.Visibility = Visibility.Collapsed;
        }

        #endregion

        #region Record

        void SaveRecord(BIURecord record)
        {
            var rpath = $"{Plan.To}/.biurecord";
            var stream = File.Create(rpath);
            new XDocument(new XComment($"Generated by Heroius.BIU, version={Assembly.GetExecutingAssembly().GetName().Version.ToString(4)}"), new XElement("BIURecord",
                new XElement("Result", record.Result.ToString()), new XElement("Time", record.BackupTime.ToOADate().ToString()), new XElement("From", record.From), new XElement("Message", record.RunMessage), new XElement("Remark", record.UserRemark))).Save(stream);
            stream.Close();
        }

        BIURecord LoadRecord()
        {
            var rpath = $"{Plan.To}/.biurecord";
            if (File.Exists(rpath))
            {
                var stream = File.Open(rpath, FileMode.Open);
                var root = XDocument.Load(stream).Element("BIURecord");
                BIURecord rec = new BIURecord();
                rec.Result = root.Element("Result").Value.As<BIURunResult>();
                rec.BackupTime = DateTime.FromOADate(root.Element("Time").Value.As<double>());
                rec.From = root.Element("From").Value;
                rec.RunMessage = root.Element("Message").Value;
                rec.UserRemark = root.Element("Remark").Value;
                stream.Close();
                return rec;
            }
            else return new BIURecord();
        }

        #endregion

        #region Variables

        private void AddVariable_Click(object sender, RoutedEventArgs e)
        {
            //todo: 在加入新变量时验证 1.现有变量名和内置变量名冲突； 2.变量值的合法字符
            Plan.Variables.Add(new ValuePair<string, string>("修改变量名", "修改值"));
        }

        private void DelVar_Click(object sender, RoutedEventArgs e)
        {
            var t = ((sender as Button).DataContext as ValuePair<string, string>);
            Plan.Variables.Remove(t);
        }

        #endregion
    }
}
